package com.demo.support.util.File;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 文件上传工具类
 * 
 * @author Yuanqy
 *
 */
public class FileFormUtil {

	private static final Logger log = LoggerFactory.getLogger(FileFormUtil.class);

	/**
	 * 获取上传各文件文件名所在域的字节数组的迭代器
	 * 
	 * @param request  请求
	 * @return 字节数组的迭代器
	 * @throws IOException
	 */
	public Iterator<Field> analysis(HttpServletRequest request) throws IOException {

		InputStream input = request.getInputStream();
		// 没有设定文件字符集的话，默认是'iso8859_1'
		String charset = UploadBytes.getCharset(request);
		// 获取表单域的分隔符
		byte[] boundarys = UploadBytes.getBoundaryStr(request).getBytes(charset);
		return new FieldIterator(input, charset, boundarys);
	}

	/**
	 * 将byte[]文件序列化到硬盘
	 * 
	 * @param buf
	 * @param filePath
	 *            路径
	 * @param fileName
	 *            文件名
	 */
	public static void byte2File(byte[] buf, String filePath, String fileName) {
		BufferedOutputStream bos = null;
		FileOutputStream fos = null;
		File file = null;
		try {
			File dir = new File(filePath);
			if (!dir.exists() && dir.isDirectory()) {
				dir.mkdirs();
			}
			file = new File(filePath + File.separator + fileName);
			fos = new FileOutputStream(file);
			bos = new BufferedOutputStream(fos);
			bos.write(buf);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 获取上传文件迭代器的实现
	 */
	private class FieldIterator implements Iterator<Field> {

		private String charset;
		private byte[] boundarys;
		private InputStream input;
		private ByteArrayOutputStream output;
		private Field currentField;
		private List<Field> currentNextFields;
		int num = 0;

		private FieldIterator(InputStream input, String charset, byte[] boundarys) {
			this.input = input;
			this.charset = charset;
			this.boundarys = boundarys;

			this.output = new ByteArrayOutputStream();
		}

		@Override
		public boolean hasNext() {
			currentField = null;// 初始当前域置为空
			// 如果一次取得域有多个
			if (currentNextFields != null && !currentNextFields.isEmpty()) {
				currentField = currentNextFields.remove(0);
				return true;
			}

			int r = -1;
			byte[] buff = new byte[1024];
			List<byte[]> fieldList = null;
			List<byte[]> fileList = null;

			try {
				while ((r = input.read(buff)) != -1) {
					output.write(buff, 0, r);

					// 获取上传文件的各域的字节列表
					fieldList = UploadBytes.findFiled(boundarys, output);

					if (fieldList != null && !fieldList.isEmpty()) {
						// 获取实际上传文件字节列表
						fileList = UploadBytes.getFilesByteList(fieldList, charset);
						if (fileList != null && !fileList.isEmpty()) {
							break;
						}
					}
				}

				if (fileList == null || fileList.isEmpty()) {
					return false;
				}
				if (fileList.size() > 1) {// 如果该长度范围有多个域时
					currentNextFields = new ArrayList<Field>(fileList.size());
				}

				Field field = null;
				for (int i = 0; i < fileList.size(); i++) {
					// 获取上传文件的实际内容的Field对象
					field = UploadBytes.getField(fileList.get(i), charset);

					if (i == 0) {// 该范围长度有一个域的情况
						currentField = field;
					} else {// 如果该长度范围有多个域的情况
						currentNextFields.add(field);
					}
				}
			} catch (Exception ex) {
				log.error("获取上传文件失败", ex);
				return false;
			}

			return currentField == null ? false : true;
		}

		@Override
		public Field next() {
			return this.currentField;
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
	}
}